import * as qs from 'qs'
import * as auth from 'auth-provider'
import { useAuth } from 'context/auth-context'

const apiUrl = process.env.REACT_APP_API_URL

interface Config extends RequestInit {
  token?: string
  data?: Object
}
// 解构不能变成可选参数，可通过赋予默认值来变成可选参数
export const http = async (endpoint: string, { data, token, headers, ...customConfig }:Config = {}) => {
  const config = {
    method: 'GET',
    headers:{
      Authorization: token ? `Bearer ${token}` : '',
      'Content-Type': data ? 'application/json' : ''
    },
    ...customConfig
  }
  if (config.method.toUpperCase() === 'GET') {
    endpoint += `?${qs.stringify(data)}`
  } else {
    config.body = JSON.stringify(data || {})
  }

  return window.fetch(`${apiUrl}/${endpoint}`, config)
    .then(async response => {
      if (response.status === 401) {
        await auth.logout()
        window.location.reload()
        return Promise.reject({ message: '请重新登录' })
      }
      const data = await response.json()
      if (response.ok) {
        return data
      } else {
        // fetch 在服务端返回的异常状态，不会发生异常，只有在断网的情况下(概率非常小)抛出异常,
        // axios 可以直接在返回状态不为2xx的时候，抛出异常
        return Promise.reject(data)
      }
    })
}

export const useHttp = () => {
  const { user } = useAuth()
  // 和http参数类型一样
  // utility type 的用法 : 用泛型给他传入一个其他类型，然后utility type对这个类型进行某种操作
  return (...[endpoint, config]: Parameters<typeof http>) => http(endpoint, { ...config, token: user?.token })
}

// 联合类型 string | number

// 类型别名

// type Person = {
//   name: string
// }
// interface Person {
//   name: string
// }
// 类型别名很多情况下可以和interface互换

// interface无法替代type的情况:
// 1、联合类型 type FavoriteNumber = string | number
// 2、联合类型 也没法实现Utility type
// type Person = {
//   name: string
//   age: number
// }
// Partial 变成可选
// const xiaoMing: Partial<Person> = { age: 8}
// Omit 删除字段
// const shenMiRen: Omit<Person, 'name'> = { age:8 }
// const shenMiRen: Omit<Person, 'name' | 'age'> = { age:8 }

// Partial的实现
// keyof 把对象类型的键值取出来，形成联合类型
type Partial<T> = {
  [P in keyof T]?: T[P]
}

// Pick 在对象类型中挑选几个键值，形成新的类型
// Exclude 去掉联合类型中的某个键